import { getDomain } from "common/scripts/common.js";
import { isPDFjsPDFViewer, detectSelect } from "../common.js";
import Channel from "common/scripts/channel.js";

// Communication channel.
const channel = new Channel();

// to indicate whether the translation button has been shown
let HasButtonShown = false;

/* store the position of selection icon to help locate the result panel*/
// store start position of the icon
let startPosition;
// store the latest position of the icon. scroll event may change the icon's position
let currentPosition;

/**
 * 创建翻译按钮的图标元素
 */
let translateButton = document.createElement("div");
let buttonImage = document.createElement("img");
buttonImage.src =
    "";
buttonImage.style.filter = "none";
translateButton.appendChild(buttonImage);
translateButton.id = "edge-translate-button";
translateButton.style.backgroundColor = "white"; // 动态设置样式以兼容Dark Reader
translateButton.addEventListener("mousedown", buttonClickHandler);
translateButton.addEventListener("contextmenu", (e) => e.preventDefault());

let originScrollX = 0; // record the original scroll X position(before scroll event)
let originScrollY = 0; // record the original scroll Y position(before scroll event)
let originPositionX = 0; // record the original X position of selection icon(before scroll event)
let originPositionY = 0; // record the original Y position of selection icon(before scroll event)
let scrollingElement = window; // store the specific scrolling element. In normal web pages, window is the scrolling object, while in pdf.js viewer, "#viewerContainer" is the scrolling element.
// store the name of scroll property according to scrollingElement(pageXOffset for window and scrollLeft for pdf.js element)
let scrollPropertyX = "pageXOffset";
let scrollPropertyY = "pageYOffset";

// this listener activated when document content is loaded
// to make selection button available ASAP
window.addEventListener("DOMContentLoaded", () => {
    // the scrolling elements in pdf files are different from normal web pages
    if (isPDFjsPDFViewer()) {
        // #viewerContainer element is the scrolling element in a pdf file
        scrollingElement = document.getElementById("viewerContainer");
        scrollPropertyX = "scrollLeft";
        scrollPropertyY = "scrollTop";
    }
    // to make the selection icon move with the mouse scrolling
    scrollingElement.addEventListener("scroll", scrollHandler);

    document.addEventListener("mousedown", () => {
        disappearButton();
        // whether user take a select action
        detectSelect(document, (event) => {
            selectTranslate(event);
        });
    });

    document.addEventListener("dblclick", (event) => {
        selectTranslate(event, true);
    });

    document.addEventListener("click", (event) => {
        // triple click
        if (event.detail === 3) {
            selectTranslate(event, true);
        }
    });

    /**
     * implement the select translate feature
     * for the implement detail, please check in the document
     * @param {MouseEvent} event mouse event of mouse up , double click or triple click
     * @param {boolean} isDoubleClick whether the event type is double click or triple click, set false by default
     */
    async function selectTranslate(event, isDoubleClick = false) {
        if (!shouldTranslate()) return;

        const inBlacklist = await isInBlacklist();
        if (inBlacklist) return;

        chrome.storage.sync.get("OtherSettings", (result) => {
            if (!result.OtherSettings) return;

            let OtherSettings = result.OtherSettings;

            // store the position which would be transferred to display.js through the Channel.
            currentPosition = [event.clientX, event.clientY];

            // Show translating result instantly.
            if (
                OtherSettings["TranslateAfterSelect"] ||
                (isDoubleClick && OtherSettings["TranslateAfterDblClick"])
            ) {
                translateSubmit();
            } else if (OtherSettings["SelectTranslate"]) {
                showButton(event);
            }
        });
    }
});

/**
 * 处理鼠标点击按钮事件
 *
 * @param {MouseEvent} event 鼠标点击事件
 */
function buttonClickHandler(event) {
    event.preventDefault();
    event.stopPropagation();
    if (event.button === 0) {
        translateSubmit();
    } else if (event.button === 2) {
        pronounceSubmit();
    }
}

/**
 * 当鼠标选中一段文字后，调用此函数，显示出翻译按钮
 */
function showButton(event) {
    // 使翻译按钮显示出来
    document.documentElement.appendChild(translateButton);
    const XBias = 10,
        YBias = 15;

    // 翻译按钮的横坐标位置: 鼠标停留位置 + x方向滚动的高度 + bias
    let XPosition = event.x + XBias;
    // 翻译按钮的纵坐标位置: 鼠标停留位置 + y方向滚动的高度 + bias
    let YPosition = event.y - YBias - translateButton.clientHeight;

    // transfer this position to display.js through the translate submission
    startPosition = [event.clientX, event.clientY];
    currentPosition = startPosition;

    // if the icon is beyond the right side of the page, we need to put the icon on the left of the cursor
    if (XPosition + translateButton.clientWidth > document.documentElement.clientWidth)
        XPosition = event.x - XBias - translateButton.clientWidth;
    // if the icon is above the top of the page, we need to put the icon below the cursor
    if (YPosition <= 0) YPosition = event.y + YBias;

    // set the new position of the icon
    // translateButton.style.transform = "translate(" + XPosition + "px," + YPosition + "px)";
    translateButton.style.top = `${YPosition}px`;
    translateButton.style.left = `${XPosition}px`;

    // record original position of the selection icon and the start mouse scrolling position
    originScrollX = scrollingElement[scrollPropertyX];
    originScrollY = scrollingElement[scrollPropertyY];
    originPositionX = XPosition;
    originPositionY = YPosition;
    HasButtonShown = true;
}

/**
 * 处理点击翻译按钮后的事件
 */
function translateSubmit() {
    // 发送消息给后台进行翻译。
    if (window.getSelection().toString().trim()) {
        channel
            .request("translate", {
                text: window.getSelection().toString(),
                // send the position of selection icon to background
                // to help locate the result panel
                position: currentPosition, // an array
            })
            .then(() => {
                chrome.storage.sync.get("OtherSettings", (result) => {
                    // to check whether user need to cancel text selection after translation finished
                    if (result.OtherSettings && result.OtherSettings["CancelTextSelection"]) {
                        cancelTextSelection();
                    }
                });
                disappearButton();
            });
    }
}

/**
 * Check if we should start translating.
 *
 * @returns {boolean} if we should start translating
 */
function shouldTranslate() {
    let selectionObject = window.getSelection();
    let selectionText = selectionObject.toString().trim();
    if (BROWSER_ENV === "firefox")
        // on firefox, we don't need to tell the focusNode type because in input elements, selectionText is ""
        return (
            selectionText.length > 0 &&
            // Do not re-translate translated text.
            !(window.isDisplayingResult && window.translateResult.originalText === selectionText)
        );

    return (
        selectionText.length > 0 &&
        // Make sure the selected text is not in input elements.
        (selectionObject.anchorNode.nodeType === Node.TEXT_NODE ||
            selectionObject.focusNode.nodeType === Node.TEXT_NODE) &&
        // Do not re-translate translated text.
        !(window.isDisplayingResult && window.translateResult.originalText === selectionText)
    );
}

/**
 * 处理发音快捷键
 */
function pronounceSubmit() {
    if (window.getSelection().toString().trim()) {
        channel.request("pronounce", {
            text: window.getSelection().toString(),
            language: "auto",
        });
    }
}

/**
 * execute this function to make the translation button disappear
 */
function disappearButton() {
    if (HasButtonShown) {
        document.documentElement.removeChild(translateButton);
        HasButtonShown = false;
    }
}

/**
 * the handler function to make the selection icon move with mouse scrolling
 * @param Event the event of scrolling
 */
function scrollHandler() {
    if (HasButtonShown) {
        let distanceX = originScrollX - scrollingElement[scrollPropertyX];
        let distanceY = originScrollY - scrollingElement[scrollPropertyY];

        translateButton.style.left = `${originPositionX + distanceX}px`;
        translateButton.style.top = `${originPositionY + distanceY}px`;
        currentPosition = [startPosition[0] + distanceX, startPosition[1] + distanceY];
    }
}

/**
 * whether the url of current page is in the blacklist
 *
 * @returns {Promise<boolean>} result in promise form
 */
function isInBlacklist() {
    return new Promise((resolve) => {
        chrome.storage.sync.get("blacklist", (result) => {
            let url = window.location.href;
            let blacklist = result.blacklist;
            return resolve(blacklist && (blacklist.domains[getDomain(url)] || blacklist.urls[url]));
        });
    });
}

/**
 * cancel text selection when translation is finished
 */
function cancelTextSelection() {
    if (window.getSelection) {
        if (window.getSelection().empty) {
            // Chrome
            window.getSelection().empty();
        } else if (window.getSelection().removeAllRanges) {
            // Firefox
            window.getSelection().removeAllRanges();
        }
    } else if (document.selection) {
        // IE
        document.selection.empty();
    }
}

/**
 * 处理取消网页翻译的快捷键
 */
function cancelPageTranslate() {
    let checkAndClick = (button) => {
        if (button !== null && button !== undefined) {
            button.click();
        }
    };

    let frame = document.getElementById(":0.container");
    if (frame !== null && frame !== undefined) {
        let cancelButton = frame.contentDocument.getElementById(":0.close");
        checkAndClick(cancelButton);
        return;
    }

    frame = document.getElementById("OUTFOX_JTR_BAR");
    if (frame !== null && frame !== undefined) {
        let cancelButton = frame.contentDocument.getElementById("OUTFOX_JTR_BAR_CLOSE");
        checkAndClick(cancelButton);
        return;
    }
}

/**
 * get selected text and its position in the page
 * @returns {Object} format: {selectedText: "string", position: [p1,p2]}
 */
function getSelection() {
    let selection = window.getSelection();
    let selectedText;
    let position;
    if (selection.rangeCount > 0) {
        selectedText = selection.toString().trim();
        const lastRange = selection.getRangeAt(selection.rangeCount - 1);
        // If the user selects something in a shadow dom, the endContainer will be the HTML element and the position will be [0,0]. In this situation, we set the position undefined to avoid relocating the result panel.
        if (lastRange.endContainer !== document.documentElement) {
            let rect = selection.getRangeAt(selection.rangeCount - 1).getBoundingClientRect();
            position = [rect.left, rect.top];
        }
    }
    return { selectedText, position };
}

// provide user's selection result for the background module
channel.provide("get_selection", () => Promise.resolve(getSelection()));

// handler for shortcut command
channel.on("command", (detail) => {
    switch (detail.command) {
        case "translate_selected":
            currentPosition = getSelection().position;
            translateSubmit();
            break;
        case "pronounce_selected":
            pronounceSubmit();
            break;
        case "cancel_page_translate":
            cancelPageTranslate();
            break;
        default:
            break;
    }
});
